﻿using ImageK.Gui;
using ImageK.Java;
using ImageK.Process;
using System;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ImageK.Plugin
{
    /** This plugin implements the Edit/Selection/Rotate command. */
    public class RoiRotator:PlugIn
    {

        private static double defaultAngle = 15;
        private static bool rotateAroundImageCenter;

        public void run(string arg)
        {
            ImagePlus imp = IJ.getImage();
            Roi roi = imp.getRoi();
            if (roi==null)
            {
                IJ.error("Rotate", "This command requires a selection");
                return;
            }
            double angle = showDialog(defaultAngle);
            if (Double.IsNaN(angle))
                return;
            if (!IJ.MacroRunning())
                defaultAngle = angle;
            FloatPolygon center = roi.getRotationCenter();
            double xcenter = center.xpoints[0];
            double ycenter = center.ypoints[0];
            if (rotateAroundImageCenter)
            {
                xcenter = imp.getWidth()/2.0;
                ycenter = imp.getHeight()/2.0;
            }
            Roi roi2 = rotate(roi, angle, xcenter, ycenter);
            if (roi2==null && (roi is ImageRoi))
                return;
            if (!rotateAroundImageCenter)
                roi2.setRotationCenter(xcenter, ycenter);
            Undo.setup(Undo.ROI, imp);
            roi = (Roi)roi.clone();
            imp.setRoi(roi2);
            Roi.setPreviousRoi(roi);
        }

        public double showDialog(double angle)
        {
            GenericDialog gd = new GenericDialog("Rotate Selection");
            int decimalPlaces = 0;
            if ((int)angle!=angle)
                decimalPlaces = 2;
            if (Macro.GetOptions()!=null)
                rotateAroundImageCenter = false;
            gd.addNumericField("Angle:", angle, decimalPlaces, 3, "degrees");
            gd.addCheckbox("Rotate around image center", rotateAroundImageCenter);
            gd.setInsets(5, 0, 0);
            gd.addMessage("Enter negative angle to \nrotate counter-clockwise", null, Color.DarkGray);
            gd.showDialog();
            if (gd.wasCanceled())
                return double.NaN;
            rotateAroundImageCenter = gd.getNextBoolean();
            return gd.getNextNumber();
        }

        public static Roi rotate(Roi roi, double angle)
        {
            if (roi is ImageRoi) {
                ((ImageRoi)roi).rotate(angle);
                return roi;
            }
            FloatPolygon center = roi.getRotationCenter();
            double xcenter = center.xpoints[0];
            double ycenter = center.ypoints[0];
            Roi roi2 = rotate(roi, angle, xcenter, ycenter);
            roi2.setRotationCenter(xcenter, ycenter);
            return roi2;
        }

        public static Roi rotate(Roi roi, double angle, double xcenter, double ycenter)
        {
            double theta = -angle*Math.PI/180.0;
            if (roi is ShapeRoi)
                return rotateShape((ShapeRoi)roi, -theta, xcenter, ycenter);
            FloatPolygon poly = roi.getFloatPolygon();
            int type = roi.getType();
            bool rotatedRect = roi is RotatedRectRoi;
            double rotatedRectWidth = 0;
            if (type==Roi.LINE)
            {
                Line line = (Line)roi;
                double x1 = line.x1d;
                double y1 = line.y1d;
                double x2 = line.x2d;
                double y2 = line.y2d;
                poly = new FloatPolygon();
                poly.addPoint(x1, y1);
                poly.addPoint(x2, y2);
            }
            else if (rotatedRect)
            {
                double[] p = ((RotatedRectRoi)roi).getParams();
                poly = new FloatPolygon();
                poly.addPoint(p[0], p[1]);
                poly.addPoint(p[2], p[3]);
                rotatedRectWidth = p[4];
            }
            for (int i = 0; i<poly.npoints; i++)
            {
                double dx = poly.xpoints[i]-xcenter;
                double dy = ycenter-poly.ypoints[i];
                double radius = Math.Sqrt(dx*dx+dy*dy);
                double a = Math.Atan2(dy, dx);
                poly.xpoints[i] = (float)(xcenter + radius*Math.Cos(a+theta));
                poly.ypoints[i] = (float)(ycenter - radius*Math.Sin(a+theta));
            }
            Roi roi2 = null;
            if (type==Roi.LINE)
                roi2 = new Line(poly.xpoints[0], poly.ypoints[0], poly.xpoints[1], poly.ypoints[1]);
            else if (rotatedRect)
                roi2 = new RotatedRectRoi(poly.xpoints[0], poly.ypoints[0], poly.xpoints[1], poly.ypoints[1], rotatedRectWidth);
            else if (type==Roi.POINT)
                roi2 = new PointRoi(poly.xpoints, poly.ypoints, poly.npoints);
            else
            {
                if (type==Roi.RECTANGLE)
                    type = Roi.POLYGON;
                if (type==Roi.RECTANGLE && poly.npoints>4) // rounded rectangle
                    type = Roi.FREEROI;
                if (type==Roi.OVAL||type==Roi.TRACED_ROI)
                    type = Roi.FREEROI;
                roi2 = new PolygonRoi(poly.xpoints, poly.ypoints, poly.npoints, type);
            }
            roi2.copyAttributes(roi);
            return roi2;
        }

        private static Roi rotateShape(ShapeRoi roi, double angle, double xcenter, double ycenter)
        {
            //todo:
            // Shape shape = roi.getShape();
            // AffineTransform at = new AffineTransform();
            // at.rotate(angle, xcenter, ycenter);
            // Rectangle r = roi.getBounds();
            // at.translate(r.X, r.Y);
            // Shape shape2 = at.createTransformedShape(shape);
            // Roi roi2 = new ShapeRoi(shape2);
            // roi2.copyAttributes(roi);
            // return roi2;

            GraphicsPath path = roi.getShapeAsGraphicsPath();
            // Create a matrix for rotation
            Matrix matrix = new Matrix();
            // Rotate the shape by degrees around its center
            matrix.RotateAt((float)angle, new PointF((float)xcenter, (float)ycenter));

            // Apply the transformation to the GraphicsPath
            path.Transform(matrix);
            Roi roi2 = new ShapeRoi(roi.parseGraphicsPathAsShape(path));
            roi2.copyAttributes(roi);
            return roi2;
        }

    }

}
